package com.liveinstars.activity.business.activity.service.impl;

import com.aliyun.openservices.log.http.client.ServiceException;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.liveinstars.activity.business.activity.entity.ActivityDO;
import com.liveinstars.activity.business.activity.enums.ActivityStatus;
import com.liveinstars.activity.business.activity.repository.facade.IActivityMembersService;
import com.liveinstars.activity.business.activity.repository.facade.IActivityService;
import com.liveinstars.activity.business.activity.repository.po.Activity;
import com.liveinstars.activity.business.activity.repository.po.ActivityMembers;
import com.liveinstars.activity.business.activity.service.ActivityService;
import com.liveinstars.api.dto.ActivityDto;
import com.liveinstars.api.dto.InterestSimpleDto;
import com.liveinstars.api.facade.IInterestApi;
import com.liveinstars.api.im.ChatGroupApi;
import com.liveinstars.api.im.ChatGroupApiService;
import com.liveinstars.api.im.vo.ChatGroupDto;
import com.liveinstars.api.query.ActivityNewQuery;
import com.onepiece.cache.service.CacheService;
import com.onepiece.shipelves.common.Result;
import com.onepiece.shipelves.common.exception.QuHuiRuntimeException;
import com.onepiece.shipelves.common.utils.CacheUtil;
import com.onepiece.shipelves.common.utils.DateUtil;
import com.onepiece.shipelves.common.utils.ServiceUtils;
import com.onepiece.shipelves.common.vo.Page;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 除了聚合根外聚合中还有领域服务service，也是主要的业务逻辑承载，负责聚合多个实体合作完成的业务逻辑，
 * 聚合中不要引用其他聚合的实体和值对象，以传参为主，为了以后拆分微服务，
 * 只用把应用接口层拆分出来。也不要调用其他聚合的领域服务，有application层调用
 */
@Service("activityService")
public class ActivityServiceImpl implements ActivityService {

    private final static Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    @Autowired
    IActivityService activityService;

    @Autowired
    IActivityMembersService activityMembersService;

    @Autowired
    ActivityFactory activityFactory;

    @Autowired
    CacheService cache;

    @Autowired
    ChatGroupApi chatGroupApi;

    @Autowired
    ChatGroupApiService chatGroupApiService;

    @Autowired
    IInterestApi interestApi;


    /**
     * 按照活动表中的字段进行查询分页，根据传入的地址
     *
     * @param query
     * @return
     */
    @Override
    public Page<ActivityDO> pageByQuery(ActivityNewQuery query) {

        IPage<Activity> iPage;
        iPage = activityService.pageByQuery(query);
        Page<ActivityDO> page = new Page<ActivityDO>(activityFactory.getActivityDOs(iPage.getRecords()),
                Long.valueOf(iPage.getCurrent()).intValue(),
                Long.valueOf(iPage.getSize()).intValue(),
                Long.valueOf(iPage.getTotal()).intValue());

        return page;
    }

    /**
     * 查询与我相关的活动，
     * 想查询我所加入的所有的小圈，然后查询我所有的小圈关联的未结束的已经发布的活动，然后判断需不要加入招募活动
     * 可以利用sql分页输出，如果难做的话就全部输出,距离排序的话就通过难以利用sql进行分页输出
     * 我觉得可以全部输出，在客户端进行筛选排序
     * 先考虑完美的分页输出
     * @param query
     * @param uid
     * @return
     */
    @Override
    public Page<ActivityDO> pageByNewQuery(ActivityNewQuery query, Integer uid) {
        //查询传入的用户加入的圈子
        List<ChatGroupDto> chatGroupByUid = chatGroupApiService.getChatGroupListByUid(Long.valueOf(query.getUserId()));
        List<Integer> groupIds = ServiceUtils.fetchPropertyList(chatGroupByUid, ChatGroupDto::getId).stream().map(i -> i.intValue()).collect(Collectors.toList());
        query.setSmallGroupIds(groupIds);
        Result<List<InterestSimpleDto>> myInterestsSimple = interestApi.getMyInterestsSimple(query.getUserId());
        List<InterestSimpleDto> data = myInterestsSimple.getData();
        if(data != null){
            List<Integer> interestids = ServiceUtils.fetchPropertyList(data, InterestSimpleDto::getId);
            query.setGroupIds(interestids);
        }

        final List<Integer> ids = new ArrayList<>();

        IPage<Activity> iPage;
        if ("distance".equals(query.getOrderField())) {
            List<Point> userLoc = cache.position("user_loc", uid + "");
            Point point = userLoc.size() > 0 ? userLoc.get(0) : null;
            //redis中维护最近的活动的距离我的列表，比进行逐条计算要快，但准确性不行
            if(point != null) {
                RedisGeoCommands.GeoRadiusCommandArgs args =
                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(1000).sortAscending();
                GeoResults<RedisGeoCommands.GeoLocation<String>> act_loc = cache.geoRadiusArgs("act_loc", point.getX(), point.getY(), 1000, args);
                act_loc.forEach(loc -> ids.add(Integer.valueOf(loc.getContent().getName())));
                query.setIds(ids);
            }
        } else {
        }
        iPage = activityService.pageByNewQueryIds(query);
        //
        Page<ActivityDO> page = new Page<ActivityDO>(activityFactory.getActivityDOs(iPage.getRecords()),
                Long.valueOf(iPage.getCurrent()).intValue(),
                Long.valueOf(iPage.getSize()).intValue(),
                Long.valueOf(iPage.getTotal()).intValue());

        return page;

    }

    @Override
    public Boolean viewActivity(Integer id) {
        Activity activity = activityService.getById(id);
        activity.setLooknum(activity.getLooknum() + 1);
        return activityService.updateById(activity);
    }

    @Override
    public Boolean likeActivity(Integer id, boolean b) {
        Activity activity = activityService.getById(id);
        if(activity == null){
            throw new QuHuiRuntimeException(ActivityExceptionEnum.ACTIVITY_IS_NOT_EXIST);
        }
        if(b){
            activity.setLikenum(activity.getLikenum() + 1);
        }else {
            activity.setLikenum(activity.getLikenum() - 1);
        }
        return activityService.updateById(activity);
    }

    @Override
    public Page<ActivityDO> getAllActivitys(ActivityNewQuery query) {
        IPage<Activity> iPage = activityService.pageByQuery(query);
        Page<ActivityDO> page = new Page<ActivityDO>(activityFactory.getActivityDOs(iPage.getRecords()),
                Long.valueOf(iPage.getCurrent()).intValue(),
                Long.valueOf(iPage.getSize()).intValue(),
                Long.valueOf(iPage.getTotal()).intValue());

        return page;
    }

    @Override
    public Boolean updateStatus(Integer activityId, int status) {
        Assert.notNull(status, "activity status must not be null");
        Assert.isTrue(!ServiceUtils.isEmptyId(activityId), "activity id must not be empty");

        Activity activity = activityService.getById(activityId);

        if (status != activity.getStatus().intValue()) {
            // Update post
            boolean success;
            if(status == ActivityStatus.INTIMATE.getValue().intValue()){
                success = activityService.removeById(activityId);
            }else {
                activity.setStatus(status);
                success = activityService.updateById(activity);
            }
            if (!success) {
                throw new ServiceException("Failed to update activity status of activity with id " + activityId);
            }

            return success;
        }
        return true;
    }

    @Override
    public Page<ActivityDO> getGroupActivitys(ActivityNewQuery query) {

        IPage<Activity> iPage = activityService.pageByQuery(query);
        Page<ActivityDO> page = new Page<ActivityDO>(activityFactory.getActivityDOs(iPage.getRecords()),
                Long.valueOf(iPage.getCurrent()).intValue(),
                Long.valueOf(iPage.getSize()).intValue(),
                Long.valueOf(iPage.getTotal()).intValue());

        return page;

    }

    @Override
    public ActivityDO getDetailById(Integer id) {
        Activity activity = activityService.getById(id);

        if (activity != null) {
            return activityFactory.getActivityDO(activity);
        }
        return null;
    }


    @Override
    public ActivityDO updateById(ActivityDO vo, Integer activityId) {
        Activity activityPO = activityFactory.createActivityPO(vo);
        activityPO.setId(activityId);
        boolean b = activityService.updateById(activityPO);
        if (b) {
            return activityFactory.getActivityDO(activityPO);
        }
        return null;
    }

    @Override
    public List<ActivityDto> getActivitys(Integer userId) {

        List<ActivityMembers> groupMemberList = activityMembersService.listByUserId(userId, 0);
        if (CollectionUtils.isNotEmpty(groupMemberList)) {
            List<Integer> chatGroupIdList = Lists.newArrayList();
            HashMap<Integer, ActivityMembers> groupMemberMap = Maps.newHashMap();
            groupMemberList.forEach(chatGroupMember -> {
                chatGroupIdList.add(chatGroupMember.getActivityId());
                groupMemberMap.put(chatGroupMember.getActivityId(), chatGroupMember);
            });
            List<Activity> chatGroupList = activityService.listByIds(chatGroupIdList);
            if (CollectionUtils.isNotEmpty(chatGroupList)) {
                List<ActivityDto> groupP1List = activityFactory.getActivityDTOs(chatGroupList);
                groupP1List.sort(Comparator.comparing(ActivityDto::getCreateTime).reversed());
//                for (InterestDto chatGroup : groupP1List) {
//                    chatGroup.setUserCount(chatGroupMemberCountMap.get(chatGroup.getId()));
//                }
                return groupP1List;
            } else {
                return Collections.emptyList();
            }
        } else {
            return Collections.emptyList();
        }
    }

    public int getIndentityId() {
        String key = CacheUtil.getSysMsgID();
        int num = cache.incr(key);
        return num;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ActivityDO createActivity(ActivityDO activityDO) {

        Activity activityPO = activityFactory.createActivityPO(activityDO);
        activityPO.setId(getIndentityId());
        activityPO.setChecked(0);
        activityPO.setHasCheckquestion(1);
        activityPO.setCreateTime(DateUtil.getNowTime());
        activityPO.setNeedcheck(1);
        activityPO.setHasCoursefile(0);
        activityPO.setDeadline(activityDO.getStartTime());
        logger.info("create activity p0 {}", activityPO.toString());
        boolean save = activityService.save(activityPO);
        if(save){
            boolean isSuccess = addActivityMember(activityPO.getId(), activityPO.getUserId());
            if(!StringUtils.isEmpty(activityPO.getLocation()) && activityPO.getLocation().contains(",")){
                String[] split = activityPO.getLocation().split(",");
                cache.geoAdd("act_loc", String.valueOf(activityPO.getId()), Double.parseDouble(split[0]), Double.parseDouble(split[1]));
            }

        }else {
            throw new QuHuiRuntimeException(ActivityExceptionEnum.ACTIVITY_CREATE_FAIL);
        }
        return activityFactory.getActivityDO(activityPO);
    }

    @Override
    public List<ActivityMembers> getJoinMembers(Integer id) {
        List<ActivityMembers> groupMemberList = activityMembersService.listByActivityId(id);
        return groupMemberList;
    }

    @Override
    public int countByActivityId(Integer activityId) {
        return activityMembersService.countByActivityId(activityId);
    }

    @Override
    public ActivityMembers getActivityMemberByActivityIdAndUserId(Integer activityId, Integer userId) {
        return activityMembersService.getActivityMemberByActivityIdAndUserId(activityId, userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addActivityMember(Integer activityId, Integer userId) {
        ActivityMembers members = new ActivityMembers();
        members.setActivityId(activityId);
        members.setUserId(userId);
        members.setCreateTime(DateUtil.getNowTime());
        return activityMembersService.addMember(members);
    }

    @Override
    public List<ActivityDO> ListActivityByIds(List<Integer> ids) {
        List<Activity> activities = activityService.listByIds(ids);
        return activityFactory.getActivityDOs(activities);
    }

}
